package com.yonyou.pmclouds.ncinvoker.invoker;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yonyou.pmclouds.basecom.impl.BillDrivenImpl;
import com.yonyou.pmclouds.basecom.impl.NCBillTypeQueryService;
import com.yonyou.pmclouds.basecom.impl.NCCSendMessageService;
import com.yonyou.pmclouds.basecom.impl.NCDepartmentQueryService;
import com.yonyou.pmclouds.basecom.runtime.RuntimeEnvironment;
import com.yonyou.pmclouds.im.util.IMHttpTookit;
import com.yonyou.pmclouds.ncinvoker.conf.NCInvokerProperties;
import com.yonyou.pmclouds.ncinvoker.entity.NCInvokerResult;
import com.yonyou.pmclouds.ncinvoker.entity.NCServiceVO;
import com.yonyou.pmclouds.ncinvoker.itf.NCInvoker;
import com.yonyou.pmclouds.ncinvoker.itf.NCInvokerCallBack;
import com.yonyou.pmclouds.ncinvoker.thread.NCInvokerTask;
import com.yonyou.pmclouds.ncinvoker.thread.NCThreadPool;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.*;

@Slf4j
public class NCUBLInvoker implements NCInvoker{

    public static final String UBL_SERVICE_SERVLET_PATH = "/gwmanage/ncopenapi/commonapi";
    public static final String NC_UBL_SERVICE_CONST = "itype";
    public static final String UBL_INFO_CONST = "info";
    public static final String UBL_STATUS_CONST = "status";
    public static final String UBL_ERROR_MSG_CONST = "errormsg";
    public static final String UBL_ERROR_CODE_CONST = "errorcode";
    public static final String UBL_DATA_CODE_CONST = "data";

    private static ObjectMapper mapper = new ObjectMapper();

    static {
        mapper.configure(JsonParser.Feature.ALLOW_MISSING_VALUES, true);
    }

    private NCInvokerProperties properties;
    private String ublServiceUrl;

    public NCUBLInvoker(NCInvokerProperties properties) {
        this.properties = properties;
        this.ublServiceUrl = properties.getUblAddress() + UBL_SERVICE_SERVLET_PATH;
    }

    @Override
    public NCInvokerResult invokeSync(NCServiceVO serviceVO, Map<String, Object> params, String tenant, String user) {
        //  集团及帐套
        params.put(NCInvoker.SERVICE_NAME_CONST, serviceVO.getServiceName());
        String serviceUrl = this.ublServiceUrl + "?" + "tenantId=" + tenant + "&appid=" + properties.getRestAppId();
        log.info("访问友企连url:{}，参数params:{}", serviceUrl, params);
        String result = null;
        try {
            Map<String, String> ublParam = new HashMap<>();
            ublParam.put(UBL_INFO_CONST, mapper.writeValueAsString(params));
            ublParam.put(NC_UBL_SERVICE_CONST, serviceVO.getServiceImpl());
            result = IMHttpTookit.doPostWithJson(serviceUrl, mapper.writeValueAsString(ublParam), null);
        } catch (JsonProcessingException e) {
            log.error("nc invoke failed: serialization failed");
            return new NCInvokerResult(NCInvokerResult.STATUS_INTERNAL_ERROR, null, null);
        } catch (Exception e) {
            return new NCInvokerResult(NCInvokerResult.STATUS_FAILED, e.getMessage(), null);
        }
        if (result == null) {
            return new NCInvokerResult(NCInvokerResult.STATUS_OK, null, null);
        }
        if (result.contains("responseCode")) {
            // TODO 这里调整一下，nc的异常返回页包含这个值
            return new NCInvokerResult(NCInvokerResult.STATUS_FAILED, result, null);
        }
        // 处理友企联的结果
        Map<String, Object> ublResult = null;
        try {
            ublResult = mapper.readValue(result, Map.class);
        } catch (IOException e) {
            return new NCInvokerResult(NCInvokerResult.STATUS_INTERNAL_ERROR, result, null);
        }
        if (ublResult == null) {
            return new NCInvokerResult(NCInvokerResult.STATUS_OK, result, null);
        }
        String status = (String) ublResult.get(UBL_STATUS_CONST);
        if (!"success".equals(status)) {
            return new NCInvokerResult(NCInvokerResult.STATUS_FAILED, (String) ublResult.get(UBL_ERROR_MSG_CONST), (String) ublResult.get(UBL_ERROR_CODE_CONST));
        }
        return new NCInvokerResult(NCInvokerResult.STATUS_OK, (String) ublResult.get(UBL_DATA_CODE_CONST), null);
    }

    @Override
    public Future<NCInvokerResult> invokeAsync(NCServiceVO serviceVO, Map<String, Object> params, String tenant, String user, final NCInvokerCallBack callBack) {
        // TODO 整个线程池？有点不太想搞线程池了，已经有两个了。。。
        return NCThreadPool.getInstance().getExecutor().submit(new NCInvokerTask(serviceVO, params, tenant, user) {

            @Override
            public NCInvokerResult call() throws Exception {
                NCInvokerResult result = NCUBLInvoker.this.invokeSync(serviceVO, this.getParams(), this.getTenant(), this.getUser());
                try {
                    if (callBack != null) {
                        RuntimeEnvironment.setLoginUserId(this.getUser());
                        RuntimeEnvironment.setTenantId(this.getTenant());
                        callBack.whenResult(result);
                    }
                } catch (Exception e) {
                    log.error("invoke nc service {} callback failed in tenant {}, error is {}", serviceVO.getServiceName(), this.getTenant(), e);
                }
                return result;
            }
        });
    }
}
